package com.hitqz.robot.biz.flow.component.patrol;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.hitqz.robot.api.business.entity.PatrolResultEntity;
import com.hitqz.robot.api.business.enums.ResultTypeEnum;
import com.hitqz.robot.api.business.enums.WorkStatusEnum;
import com.hitqz.robot.api.business.model.dto.FlowContextDto;
import com.hitqz.robot.api.business.model.dto.FlowPatrolPreAndEndTicketsDto;
import com.hitqz.robot.api.business.model.dto.FlowPatrolTicketsDto;
import com.hitqz.robot.api.business.model.vo.patrol.PatrolItemVo;
import com.hitqz.robot.api.common.entity.*;
import com.hitqz.robot.biz.core.FileProperties;
import com.hitqz.robot.biz.flow.util.LiteFlowUtil;
import com.hitqz.robot.biz.service.*;
import com.hitqz.robot.camera.EquipmentFactory;
import com.hitqz.robot.camera.IEquipmentService;
import com.hitqz.robot.camera.configure.EquipmentCmdEnum;
import com.hitqz.robot.camera.dto.MediaFileDto;
import com.hitqz.robot.camera.dto.QueryParamsDto;
import com.hitqz.robot.camera.dto.TakePicParamsDto;
import com.hitqz.robot.driver.model.IRobotPosDto;
import com.hitqz.robot.driver.parent.RobotDriverService;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.core.NodeComponent;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.web.ServerProperties;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.net.URL;
import java.util.Date;
import java.util.HashMap;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.ExecutorService;

@Slf4j
@LiteflowComponent(value = "DynamicDetectByTime", name = "[巡检]动态检测固定时间间隔")
@AllArgsConstructor
public class DynamicDetectByTime extends NodeComponent {

    private final RobotService robotService;

    private final RobotEquipmentService robotEquipmentService;

    private final EquipmentFactory equipmentFactory;

    private final PatrolItemService patrolItemService;

    private final PatrolObjectService patrolObjectService;

    private final PatrolCheckTypeService patrolCheckTypeService;


    private final PatrolResultService patrolResultService;

    private final PatrolObjectStandardService patrolObjectStandardService;

    private final ServerProperties serverProperties;

    private final RobotDriverService robotDriverService;

    private final RobotMapPosService robotMapPosService;

    private final FileProperties fileProperties;

    private final ExecutorService customThreadPool;

    @Override
    public void process() throws Exception {
        FlowContextDto dto =  this.getRequestData();
        TicketsTask task = dto.getTicketsTask();
        log.info("任务数据 {}", task.getParseOrderParams());
        FlowPatrolTicketsDto params = JSONObject.parseObject(task.getParseOrderParams(), FlowPatrolTicketsDto.class);
        log.info("{} ---params: {}", task.getParseOrderParams(), JSONObject.toJSONString(params));
        FlowPatrolPreAndEndTicketsDto endParams = JSONObject.parseObject(task.getParseOrderParams(), FlowPatrolPreAndEndTicketsDto.class);
        IRobotPosDto iRobotPosDto = robotDriverService.getWebPos();
        //走过了退出
        int currentX = (int) iRobotPosDto.getX();
        for (int i = 0; i < 20; i++) {
            try{
                Thread.sleep(1000);
                //外部中断
                LiteFlowUtil.checkInterruptFlag();
                Robot robot = robotService.getDefaultRobot();
                if (!Objects.equals(robot.getWorkStatus(), WorkStatusEnum.WORK.getCode())) {
                    log.info("非作业，退出动态检测");
                    break;
                }
                log.info("动态检测第{}次",i);
                //拍照
                String relativePath = takePic(params);
                //检测
                JSONObject result = detect(params, relativePath);
                //生成报表
                uploadReport(currentX,i, dto, params, result, relativePath);
            } catch (Exception e) {

            }
        }

    }


    private void uploadReport(Integer x,Integer times,FlowContextDto dto,FlowPatrolTicketsDto params,JSONObject jsonObject,String relativePath){
        Integer code = jsonObject.getInteger("code");
        String result = "0";
        String path = relativePath;
        String normalValue = "0";
        if (Objects.equals(code, 200)) {
            if (StrUtil.isNotBlank(jsonObject.getString("result"))) {
                result = jsonObject.getString("result").replace("a","").replace("b","").replace("c","");
            }
            if (StrUtil.isNotBlank(jsonObject.getString("path"))) {
                String visionPath = downloadImage(jsonObject.getString("path"));
                if (StrUtil.isNotBlank(visionPath)){
                    path = visionPath;
                }
            }
            if (StrUtil.isNotBlank(jsonObject.getString("normal_value")) ) {
                normalValue = jsonObject.getString("normal_value");
            }
        }
        Integer patrolItemId = params.getPatrolItemId();
        PatrolItem item = patrolItemService.getById(patrolItemId);
        PatrolObject patrolObject = patrolObjectService.getById(item.getPatrolObjectId());
        PatrolCheckType checkType = patrolCheckTypeService.getById(patrolObject.getPatrolCheckTypeId());
        PatrolObjectStandard standard = patrolObjectStandardService.getOne(Wrappers.<PatrolObjectStandard>lambdaQuery().eq(PatrolObjectStandard::getPatrolObjectId, patrolObject.getId()).last("limit 1"));
        PatrolResultEntity patrolResult = patrolResultService.getOne(Wrappers.<PatrolResultEntity>lambdaQuery()
                .eq(PatrolResultEntity::getTicketsId, dto.getTickets().getId())
                .eq(PatrolResultEntity::getItemId, params.getPatrolItemId())
                .last("limit 1")
        );
        //新增
        patrolResult.setId(null);
        patrolResult.setItemId(times);
        patrolResult.setDevName("轨道"+(x/1000)+"米处第"+times+"次");
        patrolResult.setCheckTime(new Date());
        //处理结果数据
        patrolResult.setVisionResult(result);
        patrolResult.setConvertValue(result);
        normalValue = Optional.ofNullable(standard).map(PatrolObjectStandard::getStandardValue).orElse(normalValue);
        if (Objects.equals(patrolObject.getIsConvert(), 1) && StrUtil.isNotBlank(result)) {
            // 转换后的值,用来比较和做数据统计的
            // 0-无返回 1-浮点型 2-单个枚举 3-多个枚举 4-字符串  todo 前端加5-数显
            String resultType = checkType.getResultType();
            ResultTypeEnum resultTypeEnum = ResultTypeEnum.fromType(resultType);
            if (resultTypeEnum != null) {
                switch (resultTypeEnum) {
                    //todo 新增和编辑去检查有没有 ,
                    case FLOAT -> {
                        String[] convertValues = patrolObject.getFloatConvert().split(",");
                        BigDecimal convertMinValue = new BigDecimal(convertValues[0]);
                        BigDecimal convertMaxValue = new BigDecimal(convertValues[1]);
                        BigDecimal visionResult = new BigDecimal(result);
                        double convertValue = visionResult.multiply(convertMaxValue.subtract(convertMinValue)).add(convertMinValue).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
                        patrolResult.setConvertValue(String.valueOf(convertValue));
                        // 处理正常范围
                        if (StrUtil.isNotBlank(params.getVisionNormalValue())){
                            String[] split = params.getVisionNormalValue().split(" ");
                            BigDecimal normalMinResult = new BigDecimal(split[Math.min(0,split.length-1)]);
                            BigDecimal normalMaxResult = new BigDecimal(split[Math.min(1,split.length-1)]);
                            double normalMin = normalMinResult.multiply(convertMaxValue.subtract(convertMinValue)).add(convertMinValue).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
                            double normalMax = normalMaxResult.multiply(convertMaxValue.subtract(convertMinValue)).add(convertMinValue).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
                            normalValue = normalMin +","+normalMax;
                        }
                    }
                    case SINGLE_ENUM, MULTI_ENUM -> {
                        //todo 新增和编辑去检查有没有 , 还有数量关系
                        String[] enumResults = patrolObject.getEnumResult().split(",");
                        String[] enumConverts = patrolObject.getEnumConvert().split(",");
                        for (int i = 0; i < enumResults.length; i++) {
                            if (Objects.equals(enumResults[i], result)) {
                                patrolResult.setConvertValue(enumConverts[i]);
                                break;
                            }
                        }
                    }
                    case LED -> {
                        //todo 前端要加
                        Integer afterPointCountConvert = patrolObject.getAfterPointCountConvert();
                        if (!result.contains(".") && Objects.equals(afterPointCountConvert,0)) {
                            BigDecimal value = new BigDecimal(result);
                            BigDecimal point = BigDecimal.valueOf(Math.pow(10, afterPointCountConvert));
                            double v = value.divide(point).setScale(afterPointCountConvert, BigDecimal.ROUND_HALF_UP).doubleValue();
                            patrolResult.setConvertValue(String.valueOf(v));
                        }
                    }
                }
            }
        }
        // 对比结果
        // 0-未检测 1-正常 2-异常 3-视觉识别失败
        patrolResult.setStatus(1);
        if (Objects.equals(checkType.getIsVision(), 1) && StrUtil.isBlank(result)) {
            //视觉检测项但是没有结果，判定为失败
            patrolResult.setStatus(3);
        } else if (StrUtil.isNotBlank(normalValue) && StrUtil.isNotBlank(patrolResult.getConvertValue())) {
            //都是转换后的进行对比
            switch (Optional.ofNullable(standard).map(PatrolObjectStandard::getType).orElse("-1")) {
                case "1" -> {
                    //值判定
                    patrolResult.setStatus(normalValue.contains(patrolResult.getConvertValue()) ? 1 : 2);
                }
                case "2" -> {
                    //区间判定 todo 新增和编辑去检查有没有 ,
                    String[] values = normalValue.split(",");
                    float minValue = Float.parseFloat(values[0]);
                    float maxValue = Float.parseFloat(values[1]);
                    float checkValue = Float.parseFloat(patrolResult.getConvertValue());
                    patrolResult.setStatus((minValue <= checkValue && checkValue <= maxValue) ? 1 : 2);
                }
            }
        }
        // 标准值
        patrolResult.setNormalValue(StrUtil.isNotBlank(normalValue)? normalValue.replace(",","~") + patrolObject.getUnit() : normalValue);
        // 展示在报表的值 转换后的值+单位
        String showValue =StrUtil.isBlank(patrolObject.getUnit()) ? patrolResult.getConvertValue() : StrUtil.isBlank(patrolResult.getConvertValue())? "" : patrolResult.getConvertValue() +patrolObject.getUnit();
        patrolResult.setShowValue(showValue);
        patrolResult.setMediaUrl(relativePath);
        patrolResult.setVisionMediaUrl(path);
        patrolResultService.saveOrUpdate(patrolResult);
        // 报警
        Runnable runnableTask = () -> {
            //todo 上传到服务端 0-未上传 1-已上传 2-上传失败
            patrolResultService.upload2Server(patrolResult);
            patrolResult.setUploadStatus(1);
            patrolResultService.saveOrUpdate(patrolResult);
        };
        customThreadPool.submit(runnableTask);
    }


    private JSONObject detect(FlowPatrolTicketsDto params,String relativePath){
        Integer patrolItemId = params.getPatrolItemId();
        PatrolItem patrolItem = patrolItemService.getById(patrolItemId);
        PatrolObject patrolObject = patrolObjectService.getById(patrolItem.getPatrolObjectId());
        PatrolCheckType checkType = patrolCheckTypeService.getById(patrolObject.getPatrolCheckTypeId());
        //视觉项
        String visionUrl = checkType.getVisionUrl();
        String type = checkType.getVisionType();
        Robot robot = robotService.getDefaultRobot();
        String externalIp = StrUtil.isBlank(robot.getExternalIp()) ? "127.0.0.1" : robot.getExternalIp();
        String externalHost = StrUtil.isBlank(robot.getExternalPort()) ? externalIp + ":" + serverProperties.getPort() : externalIp + ":" + robot.getExternalPort();
        String host = (visionUrl.contains("127.0.0.1") || visionUrl.contains("localhost")) ? "127.0.0.1:"+ serverProperties.getPort() : externalHost;
        // 编码，避免#等特殊符号
        String url = "http://"+ host + "/file/" + relativePath;
        HashMap<String, Object> query = new HashMap<>();
        query.put("url",url);
        query.put("rectype",Integer.parseInt(type));
        log.info("视觉地址：{},参数:{}",visionUrl,JSONObject.toJSONString(query));
        String result = "";
        try{
            result = HttpUtil.post(visionUrl, JSONObject.toJSONString(query), 20000);
        }catch (Exception e){
            log.info("视觉检测失败",e);
        }
        log.info("视觉检测结果: {}", result);
        return JSONObject.parseObject(result);
    }



    private String takePic(FlowPatrolTicketsDto params){
        RobotEquipment channel = robotEquipmentService.getById(params.getDetectEquipmentId());
        IEquipmentService service = equipmentFactory.getServiceByBrand(channel.getBrand());
        // /日期/巡检对象
        PatrolItemVo item = patrolItemService.getVoById(params.getPatrolItemId());
        String folder ="patrol" + File.separator + DateUtil.format(new Date(),"yyyyMMdd")+ File.separator + item.getPatrolObjectStr();
        //MediaFileDto mediaFileDto = service.takePic(channel.getIp(), channel.getChannel(), fileProperties.getLocal().getBasePath(), folder);
        TakePicParamsDto paramsDto = new TakePicParamsDto();
        paramsDto.setBasePath(fileProperties.getLocal().getBasePath());
        paramsDto.setFolder(folder);
        QueryParamsDto queryParamsDto = new QueryParamsDto();
        queryParamsDto.setPassword(channel.getPassword());
        queryParamsDto.setUsername(channel.getUsername());
        queryParamsDto.setPort(channel.getPort());
        queryParamsDto.setChannel(channel.getChannel());
        queryParamsDto.setIp(channel.getIp());
        MediaFileDto mediaFileDto = (MediaFileDto) service.executeCmd(queryParamsDto, true, EquipmentCmdEnum.TAKE_PIC,paramsDto);
        log.info("拍照结果 {}", mediaFileDto);
        return mediaFileDto.getRelativePath();
    }


    /**
     * 下载图片到本地
     * @param imageUrl 视觉 url
     * @return url 本地的相对路径
     */
    private String downloadImage(String imageUrl) {
        InputStream in = null;
        String relativePath =  File.separator + "vision_draw" + File.separator + DateUtil.format(new Date(),"yyyyMMdd") + File.separator + DateUtil.format(new Date(),"yyyyMMddHHmmssSSS") + ".jpg";
        String path = fileProperties.getLocal().getBasePath() + relativePath;
        try  {  // 打开输入流
            URL url = new URL(imageUrl.replace("/./","/"));  // 创建 URL 对象
            in = url.openStream();
            FileUtil.writeFromStream(in,path);
            return relativePath;
        } catch (IOException e) {
            log.error("down vision picture error:{}",e.getMessage());
            return "";
        } finally {
            try {
                if (in!=null) {
                    in.close();
                }
            } catch (IOException e) {
                log.error("InputStream close error:{}",e.getMessage());
            }
        }
    }



}